const path = require('path');

process.env.LOG_FILENAME = path.join(__dirname, 'fetch');

const fs = require('fs');
const { Client } = require('ssh2');
const spark = require(`${process.env.SPARK_HOME}/ecma-/spark`);
const RTable = spark.spark.RTable;
const os = require('os');
const dayjs = require('dayjs');

const { logger } = require('../src/logging');

const RELAY_SERVER = {
  host: '106.14.253.154',
  port: 22,
  username: 'root',
  privateKey: fs.readFileSync(`${process.env.HOME}/.ssh/id_rsa`)  //使用私钥进行登陆，.ssh/id_rsa
};
const WORKDIR = 'rkt';
const LOCAL_FILE = path.join(__dirname, '.upload.json'); // .upload.json

// 遥测
const table_analog = new RTable('scada_analog');
// 遥信
const table_signal = new RTable('scada_signal');
// 电度
// const table_degree = new RTable('scada_degree');
// // 遥控
// const table_control = new RTable('scada_control');
// // 遥调
// const table_set = new RTable('scada_set');


const conn = new Client();
let sftp;

function fastPut(localPath, remotePath) {
  return new Promise((resolve, reject) => {
    sftp.fastPut(localPath, remotePath, (err) => {        // 上传文件：本地路径 -> 远端完整文件路径
      if (err) {
        reject(err);
        return;
      }

      resolve();
    });
  });
}

function rename(srcPath, destPath) {
  return new Promise((resolve, reject) => {
    sftp.rename(srcPath, destPath, (err) => {
      if (err) {
        logger.error(`SFTP重命名失败: ${err.message}, 源路径: ${srcPath}, 目标路径: ${destPath}`);
        reject(err);
        return;
      }

      resolve();
    });
  });
}

function deleteRemoteFile(filePath) {
  return new Promise((resolve, reject) => {
    sftp.unlink(filePath, (err) => {
      if (err) {
        logger.error(`删除远程文件失败: ${err.message}, 文件路径: ${filePath}`);
        reject(err);
        return;
      }
      logger.info(`成功删除远程文件: ${filePath}`);
      resolve();
    });
  });
}

function execCmd(command, captureOutput = false) {
  logger.debug('Exec: ' + command);
  return new Promise((resolve, reject) => {
    conn.exec(command, (err, stream) => {
      if (err) {
        reject(err);
        return;
      }

      let stdout = '';
      let stderr = '';

      stream.on('close', (code, signal) => {
        logger.debug('Stream :: close :: code: ' + code + ', signal: ' + signal);
        if (captureOutput) {
          resolve(stdout);
        } else {
          resolve(code);
        }
      }).on('data', (data) => {
        const output = data.toString();
        stdout += output;
        logger.debug('STDOUT: ' + output);
      }).stderr.on('data', (data) => {
        const errorOutput = data.toString();
        stderr += errorOutput;
        logger.debug('STDERR: ' + errorOutput);
      });
    });
  });
}

// 清理远程目录中的所有.tmp临时文件
async function cleanupTempFiles() {
  try {
    // logger.info(`清理远程目录 ${WORKDIR} 中的临时文件`);
    const tempFilesPattern = `${WORKDIR}/*.tmp`;
    const listCommand = `ls -1 ${tempFilesPattern}`;
    logger.debug(`执行命令查找临时文件: ${listCommand}`);
    const fileList = await execCmd(listCommand, true);

    if (fileList && fileList.trim()) {
      const files = fileList.trim().split('\n');
      logger.info(`发现 ${files.length} 个遗留的.tmp临时文件, 列表: ${files.join(', ')}`);

      // 删除所有临时文件
      const deletePromises = files.map(async (file) => {
        try {
          if (file.endsWith('.tmp')) {
            logger.info(`尝试删除临时文件: ${file}`);
            await deleteRemoteFile(file);
            logger.info(`成功删除临时文件: ${file}`);
          } else {
            logger.warn(`跳过非临时文件: ${file}`);
          }
        } catch (err) {
          logger.warn(`清理文件 ${file} 失败: ${err.message}`);
        }
      });

      await Promise.all(deletePromises);
      logger.info('临时文件清理完成');
    } else {
      logger.info('没有发现需要清理的临时文件');
    }
  } catch (err) {
    logger.error(`清理临时文件过程中发生错误: ${err.message}，堆栈信息: ${err.stack}`);
  }
}

async function doTask() {
  // await execCmd('ls -1 *.json | sort -r | tail -n +101 | xargs rm -f'); // 本地未及时解析时，最多在服务器保留最新10个json文件


  const data = { analog: {}, signal: {}, ts: Date.now() }   // 创建获取数据的对象，可模拟数据进行测试

  // const maxPoints = 500;
  // const analogs = table_analog.list().slice(0, maxPoints);
  const analogs = table_analog.list();
  for (const pt of analogs) {
    data.analog[pt.name] = pt.value;
  }
  // const signals = table_signal.list().slice(0, maxPoints);
  const signals = table_signal.list();
  for (const pt of signals) {
    data.signal[pt.name] = pt.value;
  }

  // fs.writeFileSync(LOCAL_FILE, JSON.stringify(data));

  await fs.promises.writeFile(LOCAL_FILE, JSON.stringify(data));  // await

  const timestamp = dayjs().format('YYYYMMDDHHmmss.SSS');
  const filename = `output${timestamp}.json`;
  const tmpfilename = filename + '.tmp';
  const tmpFilePath = `${WORKDIR}/${tmpfilename}`;
  const finalFilePath = `${WORKDIR}/${filename}`;

  try {
    // 1. 首先尝试清理之前可能遗留的临时文件
    // await cleanupTempFiles();

    // 2. 上传新的临时文件
    logger.info(`开始上传临时文件 ${LOCAL_FILE} 到 ${tmpFilePath}...`);
    await fastPut(LOCAL_FILE, tmpFilePath);

    // 短暂延迟，确保文件完全写入
    // logger.info(`文件写入中...`);
    await new Promise(resolve => setTimeout(resolve, 100));

    logger.info(`临时文件 ${tmpfilename} 上传成功`);

    // 检查临时文件是否存在
    try {
      await sftp.stat(tmpFilePath);
      // logger.info(`临时文件 ${tmpfilename} 存在，准备执行重命名操作...`);
    } catch (statErr) {
      logger.error(`临时文件 ${tmpfilename} 上传后不存在: ${statErr.message}`);
      throw statErr; // 抛出错误，进入外层catch处理
    }

    // 执行重命名操作
    logger.info(`开始执行重命名操作: ${tmpfilename} --> ${filename}`);
    await rename(tmpFilePath, finalFilePath);
    logger.info(`文件 ${tmpfilename} --> ${filename} 重命名成功`);

    // 检查重命名后的文件是否存在
    try {
      await sftp.stat(finalFilePath);
      logger.info(`文件 ${filename} 上传成功`);
    } catch (statErr) {
      logger.error(`重命名后，文件 ${filename} 不存在: ${statErr.message}`);
    }

    logger.info('开始清理.tmp临时文件...');
    await cleanupTempFiles();

    // 保留最新的10个JSON文件，删除旧文件, 防止parse 端 解析脚本因为错误停止运行导致.json文件累积过多
    logger.info('开始清理旧的JSON文件, 但保留最新的10个');
    try {
      // 确保在WORKDIR目录下执行命令
      const cleanupCommand = `cd ${WORKDIR} && ls -1 *.json | sort -r | tail -n +11 | xargs rm -f`;
      logger.info(`执行文件清理命令: ${cleanupCommand}`);
      const exitCode = await execCmd(cleanupCommand);
      if (exitCode === 0) {
        logger.info('旧文件清理完成');
      } else {
        logger.warn(`文件清理命令执行完成, 但退出码非0: ${exitCode}`);
      }
    } catch (cleanupErr) {
      logger.error(`文件清理过程中发生错误: ${cleanupErr.message}`);
    }

  } catch (error) {
    logger.error(`文件上传过程中发生错误: ${error.message}`);

    // 无论何种错误，都尝试删除临时文件
    try {
      // 先检查临时文件是否存在且确实是.tmp文件
      if (tmpfilename.endsWith('.json.tmp')) {
        try {
          await sftp.stat(tmpFilePath);
          await deleteRemoteFile(tmpFilePath);
        } catch (deleteErr) {
          logger.error(`尝试删除临时文件 ${tmpfilename} 失败: ${deleteErr.message}`);

          // 如果SFTP删除失败，尝试使用命令行删除作为备选方案
          try {
            // 在Linux中使用正斜杠构建路径
            const linuxTmpFilePath = tmpFilePath.replace(/\\/g, '/');
            const deleteCommand = `rm -f "${linuxTmpFilePath}"`;
            const exitCode = await execCmd(deleteCommand);
            if (exitCode === 0) {
              logger.info(`通过命令行成功删除临时文件 ${tmpfilename}`);
            } else {
              logger.warn(`通过命令行删除临时文件 ${tmpfilename} 命令执行完成, 但退出码非0: ${exitCode}`);
            }
          } catch (cmdDeleteErr) {
            logger.error(`通过命令行尝试删除临时文件 ${tmpfilename} 也失败: ${cmdDeleteErr.message}`);
          }
        }
      }
    } catch (err) {
      logger.error(`临时文件删除过程中发生意外错误: ${err.message}`);
    }
  }
  // throw error;
}


/*
 精确到毫秒可以使用 YYYYMMDDHHmmss.SSS 来表示
 parse日志出现很多未更名的.tmp文件且数量一直在上升，--检查是否fetch运行是有进程卡住，在日志文件中作出标记
*/
// 只保留最新的100个文件，先不删除，会丢失历史数据, 实际上程序正常运行并不会留下这么多文件，
// await execCmd('ls -1 *.json | sort -r | tail -n +101 | xargs rm -f');
logger.info(`****** 执行时间：${dayjs().format('YYYYMMDDHHmmss.SSS')} ******`);
conn.on('ready', () => {
  logger.info('SSH连接已建立, 开始上传文件...');

  conn.sftp(async (err, sftpClient) => {
    if (err) {
      logger.error('创建SFTP连接失败: ' + err);
      conn.end(); // 出错时关闭连接
      return;
    }

    sftp = sftpClient;
    try {
      await doTask();
    } catch (taskErr) {
      logger.error(`任务执行过程中发生未捕获错误: ${taskErr.message}`);
    } finally {
      // 确保连接被关闭
      conn.end();
    }
  });
}).on('error', (err) => {
  logger.error('SSH连接错误: ' + err);
}).on('end', () => {
  logger.info('SSH连接已关闭');
}).connect(RELAY_SERVER);
